Next.jsでCloudscape Design Systemを試してみた

Next.jsでCloudscape Design Systemを試してみた

Clock Icon2022.10.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

しばたです。

前の記事でNext.jsを試す環境を作ったので本記事ではCloudscapeを実際に試してみます。

https://dev.classmethod.jp/articles/try-next-js-tutorial-on-wsl2-202210/

Cloudscape Design Systemとは?

Cloudscape Design SystemはAWSが作成しオープンソースで公開しているWEBデザインシステムです。

https://cloudscape.design/

もともとAWSが内部向けに作成し改善していたものが今年の7月に一般公開されました。
雑に言ってしまえば「マネジメントコンソール風のUIをつくれるやつ」なんですが、デザインシステムの名の通りUIコンポーネント以外にデザインパターンやデモも公開されています。

注意事項

本記事ではこちらの手順に従い最初の一歩を試してみようと思っていました。

ただ、手順をよく読んでみると最後の最後に、

Bundling with Next.js (optional)
There are known limitations when using Cloudscape with the React Framework Next.js . To follow the discussion, see the corresponding issue on GitHub .

という事でNext.jsでの利用には制限がある旨が記載されていました...
具体的な話はGitHub Discussionsで議論されています。

https://github.com/cloudscape-design/components/discussions/86

現時点ではNext.jsでCloudscapeを利用する際にnext-transpile-modulesによるトランスパイルが必要なのと、警告ログが出るなどの不都合があるそうです。

試してみた

という事で実環境での利用はちょっと難しそう[1]ですが、まあ、やるだけやってみます。

検証環境

検証環境は前回の記事で作成したNode.js 18.12.0 on WSL2(Ubuntu 20.04)をそのまま使います。

Next.jsアプリケーションの用意

まずはベースとなるアプリケーションの雛形を用意します。
~/src/ディレクトリでcloudscape-sampleという名前のアプリケーションを作成します。

# ディレクトリ移動
cd ~/src/

# Next.jsアプリケーション cloudscape-sample を作成
npx create-next-app@latest cloudscape-sample --use-npm --ts

なんとなくTypeScriptアプリケーションにしてみました。

Reactコンポーネントのインストール

チュートリアルとは違い新規アプリケーションを作るだけなのでエラーにはならないはずです。
作成されたcloudscape-sampleディレクトリに移動し、

  • @cloudscape-design/global-styles
  • @cloudscape-design/components

の2種のコンポーネントをインストールしてやります。
また、先述の通りNext.jsの場合はトランスパイルが必要なため

  • next-transpile-modules
  • next-compose-plugins

の2つのコンポーネントもインストールしてやります。
こちらは開発用のため--save-dev(-D)パラメーター付きでインストールします。

# アプリケーションディレクトリへ移動
cd ./cloudscape-sample

# Reactコンポーネントをインストール
npm install @cloudscape-design/global-styles
npm install @cloudscape-design/components

# トランスパイル関連のモジュールは --save-dev インストール
npm install -D next-transpile-modules next-compose-plugins

エラー無くインストールできればOKです。

今回は@cloudscape-design/componentsのインストール時にいくつか依存関係周りの警告が出たのとd3-colorが脆弱性のあるバージョンである旨の警告がでましたが、現状打つ手が無い脆弱性だったので無視して続行することにします...

# @cloudscape-design/componentsインストール時に警告が出る
$ npm install @cloudscape-design/components
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency
npm WARN ERESOLVE overriding peer dependency

added 37 packages, and audited 276 packages in 11s

80 packages are looking for funding
  run `npm fund` for details

4 high severity vulnerabilities

Some issues need review, and may require choosing
a different dependency.

Run `npm audit` for details.

# また、d3-color Ver.3.1.0の脆弱性が出ているがVer.3.1.0が最新バージョンのためどうすることもできない...
$ npm audit
# npm audit report

d3-color  <3.1.0
Severity: high
d3-color vulnerable to ReDoS - https://github.com/advisories/GHSA-36jr-mh4h-2g58
No fix available
node_modules/d3-color
  d3-interpolate  0.1.3 - 2.0.1
  Depends on vulnerable versions of d3-color
  node_modules/d3-interpolate
    d3-scale  0.1.5 - 3.3.0
    Depends on vulnerable versions of d3-interpolate
    node_modules/d3-scale
      @cloudscape-design/components  *
      Depends on vulnerable versions of d3-scale
      node_modules/@cloudscape-design/components

4 high severity vulnerabilities

Some issues need review, and may require choosing
a different dependency.

next.config.jsの書き換え

次にNext.jsの場合はトランスパイルのためにnext.config.jsの内容を変更してやる必要があります。

next.config.jsの内容を以下の様に変えておきます。

next.config.js (TypeScript)
// TypeScriptの場合
const withTM = require('next-transpile-modules')(['@cloudscape-design/components']);

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
}

const buildConfig = _phase => {
  const plugins = [withTM]
  const config = plugins.reduce((acc, next) => next(acc), {
    ...nextConfig
  })
  return config
}

module.exports = buildConfig();

ちなみにJavaScriptの場合は以下の様にするそうです。

next.config.js (JavaScript)
// JavaScriptの場合
const withTranspileModules = require('next-transpile-modules')
const withPlugins = require('next-compose-plugins')

const nextConfig = {
  reactStrictMode: true,
  swcMinify: true,
}

// Here we will add a plugin to our configuration to transpile the CloudScape components into
module.exports = withPlugins([withTranspileModules(['@cloudscape-design/components'])], nextConfig)

アプリケーションの記述

あとはアプリケーションコードをよしなに書いてやります。
今回は元の手順通りにpages/index.tsxの内容を以下の様に変えてみます。

pages/index.tsx
import { useState } from "react";
import Header from "@cloudscape-design/components/header";
import Container from "@cloudscape-design/components/container";
import SpaceBetween from "@cloudscape-design/components/space-between";
import Input from "@cloudscape-design/components/input";
import Button from "@cloudscape-design/components/button";

export default function App() {
  const [value, setValue] = useState("");

  return (
    <SpaceBetween size="m">
      <Header variant="h1">Hello World!</Header>

      <Container>
        <SpaceBetween size="s">
          <span>Start editing to see some magic happen</span>
          <Input
            value={value}
            onChange={(event) => setValue(event.detail.value)}
          />
          <Button variant="primary">Click me</Button>
        </SpaceBetween>
      </Container>
    </SpaceBetween>
  );
}

また、デフォルトで作成された使わないファイル群を削除しておきます。

  • pages/_app.tsx
  • pages/api/ ディレクトリ
  • styles/globals.css
  • styles/Home.module.css

この状態でnpm run devコマンドを実行し開発サーバーを動作させてみるとこんな感じで各コンポーネントが表示されます。

見かけ上は良い感じに見えますが、コンソール上では以下の様なuseLayoutEffectに関する警告ログが出ています。

Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.

GitHub Discussionsの議論を見る限り無害な警告らしいですがあまり良い気はしないですね。
一応_app.tsxに以下の記述を追加して無理やり警告を非表示にすることは出来る様です。

import React from 'react'
if (typeof window === 'undefined') React.useLayoutEffect = () => {}

記述例

pages/_app.tsx
import type { AppProps } from 'next/app'
import React from 'react'

if (typeof window === 'undefined') React.useLayoutEffect = () => {}

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />
}

その他コンポーネントとデモ

ここまでの手順で最低限の動作確認ができました。

その他のコンポーネントに関しては以下のドキュメントに詳細が記載されています。

各コンポーネントに対してPlaygroundでのサンプルコードが確認できたりかなり充実した内容になっています。

加えて多くのデモがソースコード付きで公開されています。

あとはこれらをお手本にして試行錯誤すれば望み通りのサイトが作れる...はずです。多分...

最後に

簡単ですが以上となります。

とりあえず基本的なところまでは実現できましたが、これを応用し望みのレイアウトを実現するまではもうしばらくの時間がかかりそうです。

脚注
  1. 少なくともNext.js初心者の私には厳しい... ↩︎

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.